//----------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2012 James Whitworth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the \"Software\"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//----------------------------------------------------------------------------------------------------------------------

local license_text =
@"// Copyright (c) %u %s
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
"

//----------------------------------------------------------------------------------------------------------------------
// blah
//----------------------------------------------------------------------------------------------------------------------
class CustomFileContent
{
  owner = null
  header_data = null
  inline_data = null
  source_data = null

  constructor()
  {
  }

  function has_header_data()
  {
    return type(header_data) == "string" && header_data.len() > 0
  }

  function has_inline_data()
  {
    return type(inline_data) == "string" && inline_data.len() > 0
  }

  function has_source_data()
  {
    return type(source_data) == "string" && source_data.len() > 0
  }

  function write_declaration(stream)
  {
    if (has_header_data())
    {
      stream.add_separator()
      stream.write(header_data)
      stream.new_line()

      return true
    }
    return false
  }

  function write_definition(stream, file_type)
  {
    if (file_type == "inline")
    {
      if (has_inline_data())
      {
        stream.add_separator()
        stream.write(inline_data)
        stream.new_line()

        return true
      }
    }
    else if (file_type == "source")
    {
      if (has_source_data())
      {
        stream.add_separator()
        stream.write(source_data)
        stream.new_line()

        return true
      }
    }

    return false
  }
}

//----------------------------------------------------------------------------------------------------------------------
// File class for output a code file
//----------------------------------------------------------------------------------------------------------------------
class File
{
  name = null
  author = null
  include_path = null
  namespace = null

  header_includes = null
  source_includes = null
  contents = null
  // contents that exists outside the namespace
  external_contents = null

  constructor(_name, _include_path = "", _author = "")
  {
    name = _name
    author = _author
    include_path = _include_path
    namespace = ""

    header_includes = array(0)
    source_includes = array(0)
    contents = array(0)
    external_contents = array(0)
  }

  function has_header_data()
  {
    foreach (item in contents)
    {
      if (item.has_header_data())
      {
        return true
      }
    }

    return false
  }

  function has_inline_data()
  {
    foreach (item in contents)
    {
      if (item.has_inline_data())
      {
        return true
      }
    }

    return false
  }

  function has_source_data()
  {
    foreach (item in contents)
    {
      if (item.has_source_data())
      {
        return true
      }
    }

    return false
  }

  function write_header(stream)
  {
    write_include_guard_begin(stream, "h")
    write_copyright(stream)

    local include_count = header_includes.len()
    for (local i = 0; i != include_count; ++i)
    {
      stream.write("#include \"%s\"", header_includes[i])
      stream.new_line()
    }

    if (include_count > 0)
    {
      stream.add_separator()
    }

    stream.new_line()
    write_auto_generated_warning(stream)
    stream.new_line()

    write_namespace_begin(stream)
    foreach (object in contents)
    {
      object.write_declaration(stream)
      stream.new_line()
    }
    write_namespace_end(stream)

    // write contents external to the namespaces
    //
    if (external_contents.len() > 0)
    {
      foreach (object in external_contents)
      {
        stream.new_line()
        object.write_declaration(stream)
      }
    }

    if (has_inline_data())
    {
      stream.new_line()

      assert(typeof(include_path) == "string")
      assert(typeof(name) == "string")
      assert(name.len() > 0)

      local include = null
      if (include_path.len() > 0)
      {
        include = format("%s/%s.inl", include_path, name)
      }
      else
      {
        include = format("%s.inl", name)      
      }

      write_include(stream, include)
    }

    write_include_guard_end(stream, "h")
  }

  function write_inline(stream)
  {
    write_include_guard_begin(stream, "inl")
    write_copyright(stream)
    
    stream.new_line()
    write_auto_generated_warning(stream)
    stream.new_line()

    write_namespace_begin(stream)
    foreach (object in contents)
    {
      if (object.write_definition(stream, "inline"))
      {
        stream.new_line()
      }
    }
    write_namespace_end(stream)

    // write contents external to the namespaces
    //
    if (external_contents.len() > 0)
    {
      foreach (object in external_contents)
      {
        if (object.has_inline_data())
        {
          stream.new_line()
          object.write_definition(stream, "inline")
        }
      }
    }

    write_include_guard_end(stream, "inl")
  }

  function write_source(stream)
  {
    write_copyright(stream)

    local wrote_header = false
    if (has_header_data())
    {
      wrote_header = true
      
      assert(typeof(include_path) == "string")
      assert(typeof(name) == "string")
      assert(name.len() > 0)

      local include = null
      if (include_path.len() > 0)
      {
        include = format("%s/%s.h", include_path, name)
      }
      else
      {
        include = format("%s.h", name)
      }
      write_include(stream, include)
    }

    foreach (include in source_includes)
    {
      wrote_header = true

      if (!include.find("Precompiled"))
      {
        stream.write("#include \"%s\"", include)
        stream.new_line()
      }
    }

    if (wrote_header)
    {
      stream.add_separator()
    }

    stream.new_line()
    write_auto_generated_warning(stream)
    stream.new_line()

    write_namespace_begin(stream)
    foreach (object in contents)
    {
      if (object.write_definition(stream, "source"))
      {
        stream.new_line()
      }
    }
    write_namespace_end(stream)

    // write contents external to the namespaces
    //
    if (external_contents.len() > 0)
    {
      foreach (object in external_contents)
      {
        if (object.has_source_data())
        {
          stream.new_line()
          object.write_definition(stream, "source")
        }
      }
    }
  }

  function write_copyright(stream)
  {
    stream.add_separator()
    stream.write(license_text, date().year, author)
    stream.add_separator()
  }
  
  function write_include_guard_begin(stream, file_extension)
  {
    stream.write("#pragma once")
    stream.new_line()
  }

  function write_include_guard_end(stream, file_extension)
  {
  }
  
  function write_auto_generated_warning(stream)
  {
    stream.add_separator()
    stream.write("// THIS FILE IS AUTO GENERATED DO NOT EDIT.")
    stream.new_line()
    stream.add_separator()
  }

  function write_namespace_begin(stream)
  {
    if (typeof(namespace) == "string" && namespace.len() > 0)
    {
      stream.write("namespace %s", namespace)
      stream.new_line()
      stream.write("{")
      stream.new_line()
      stream.add_separator()
    }
    stream.write("namespace autogen", namespace)
    stream.new_line()
    stream.write("{")
    stream.new_line()
  }

  function write_namespace_end(stream)
  {
    stream.write("} // namespace autogen", namespace)
    stream.new_line()
    if (typeof(namespace) == "string" && namespace.len() > 0)
    {
      stream.new_line()
      stream.write("} // namespace %s", namespace)
      stream.new_line()
    }
  }

  function write_include(stream, file_path)
  {
    assert(typeof(file_path) == "string")
    if (file_path.len() > 0)
    {
      foreach (include in source_includes)
      {
        if (include.find("Precompiled"))
        {
          stream.write("#include \"%s\"", include)
          stream.new_line()
        }
      }
      stream.write("#include \"%s\"", file_path)
      stream.new_line()
    }
  }

  function build_include_guard_define(file_extension)
  {
    assert(typeof(name) == "string")
    assert(name.len() > 0)

    assert(typeof(file_extension) == "string")
    assert(file_extension.len() > 0)

    local include_guard_define = format("_%s_%s_", name.toupper(), file_extension.toupper())

    if (typeof(namespace) == "string" && namespace.len() > 0)
    {
      include_guard_define = format("_%s%s", namespace.toupper(), include_guard_define)
    }

    return include_guard_define
  }
}